;*************************************************************
;
;       ADAMnet Hard Disk controller code for IDE hard disk
;       drive on Micro Innovations ADAMnet IDE controller.
;
;       ADAMnet I/O code is from DBL4.ASM program by Tony
;       Morehen and Guy Cousineau. Hard disk I/O code is 
;       original work by Mark Gordon of Micro Innovations.
;
;       Creation date:  June 22, 1995
;       Authors:        As described above
;       Copyright:      AJM Software (ADAMnet code) and
;                       Micro Innovations (Disk I/O code)
;       Revision:       Original
;
;*************************************************************

DRVNO   EQU     4               ;Set drive address to 4

;
;       Hard Disk register addresses
;

WRRESET EQU     1800H   ;Write causes a pulse on HD's RESET line
RDERROR EQU     0801H   ;Error register (Read)
WRPRECP EQU     1801H   ;Write Precompenstion register (Write)
RDSECCT EQU     0802H   ;Sector Count register (Read)
WRSECCT EQU     1802H   ;Sector Count register (Write)
RDSECNO EQU     0803H   ;Sector Number register (Read)
WRSECNO EQU     1803H   ;Sector Number register (Write)
RDCYLLO EQU     0804H   ;Cylinder register low byte (Read)
WRCYLLO EQU     1804H   ;Cylinder register low byte (Write)
RDCYLHI EQU     0805H   ;Cylinder register high byte (Read)
WRCYLHI EQU     1805H   ;Cylinder register High byte (Write)
RDSDH   EQU     0806H   ;Size, Drive, Head register (Read)
WRSDH   EQU     1806H   ;Size, Drive, Head register (Write)
RDSTAT  EQU     0807H   ;Status register (Read)
WRCMD   EQU     1807H   ;Command register (Write)
RDDATLO EQU     0808H   ;Data register low byte (Read)
WRDATLO EQU     1808H   ;Data register low byte (Write)
RDDATHI EQU     0809H   ;Data register high byte (Read)
WRDATHI EQU     1809H   ;Data register high byte (Write)
WRCONT  EQU     180EH   ;Fixed Disk Control register (Write)

;
;       Hard Disk Controller commands
;

HRECAL  EQU     10H     ;Home heads to Cylinder 0
HREAD   EQU     20H     ;Read sector(s) from drive
HWRITE  EQU     30H     ;Write sector(s) to drive
HREADV  EQU     40H     ;Read verify sector(s)
HFORMAT EQU     50H     ;Format track
HSEEK   EQU     70H     ;Seek cylinder
HDIAG   EQU     90H     ;Perform self test & report
HSTPARM EQU     91H     ;Set drive parameters
HSCANID EQU     92H     ;Get current head position
HIDDRV  EQU     0ECH    ;Identify drive
HRESET  EQU     4       ;Sent to control register to reset drive
HNOIRQ  EQU     2       ;Sent to control register to disable interrupt

;
;       680X Internal RAM locations
;

P1DDR   EQU     00H     ;port 1 data direction register
P2DDR   EQU     01H     ;port 2 data direction register
P1DATA  EQU     02H     ;port 1 data register
P2DATA  EQU     03H     ;port 2 data register
P4DDR   EQU     05H     ;port 4 data direction register
TCSR    EQU     08H     ;timer control and status register
COUNTER EQU     09H     ;counter register
OUTCMP  EQU     0BH     ;output compare register
RMCR    EQU     10H     ;SCI rate and mode control register
SCICSR  EQU     11H     ;SCI Tx/Rx control and status register
SCIRXD  EQU     12H     ;SCI Rx data register
SCITXD  EQU     13H     ;SCI Tx data register
RAMCR   EQU     14H     ;RAM control register

;
;       680X internal RAM storage definitions
;       (SP gets set at 00EC - What is ED thru FF used for?)
;

	ORG     80H

CURMSG  DS      5       ;command message buffer (send/rcv instructions )
LSTMSG  DS      5       ;last command buffer
STRLEN  DS      2       ;message length
BLKLOC  DS      2       ;where in memory to get/save message
CHKSUM  DS      1       ;calculated checksum
MSTAT   DS      4       ;status message buffer
;
;       Primary, Secondary and combined device status codes:
;       01 = No error              04 = Missing Media error
;       02 = CRC error             05 = Missing Drive error
;       03 = Missing Block error   06 = Write Protect error
;
CSTAT   DS      1       ;combined status (PSTAT in lower
			; nibble, SSTAT in upper nibble)
PSTAT   DS      1       ;primary status
SSTAT   DS      1       ;secondary status
;
L0096   DS      2       ;Purpose Unknown
DEVNUM  DS      1       ;device node ID
CMDSTG  DS      5       ;command stage for multi-byte commands
DATREQ  DS      1       ;data requested flag
RDWRT   DS      1       ;read(=0BH)/write(=0CH) flag
THREEB  DS      1       ;three byte block number flag
HSPT    DS      2       ;number of host sectors/track (2 Bytes)
HNHSPT  DS      2       ;no of host HDS * SPT (two bytes)
REQHD   DS      1       ;requested head number
REQSEC  DS      1       ;requested sector number
REQCYL  DS      2       ;requested cylinder number (2 bytes)

;       I/O buffer addresses (same memory addressed
;       at 1400H for read and 0400H for write)

BLKWRT  EQU     400H    ;write to external memory
BLKRD   EQU     1400H   ;read from external memory

;       Hard drive configuration data
;       (Valid only when buffer contains block 0)

PTRACK  EQU   BLKRD+3   ;No of HD cylinders
PSEEK   EQU   BLKRD+5   ;No of HD heads
PSPT    EQU   BLKRD+6   ;No of HD sec/track
PNHSPT  EQU   BLKRD+7   ;Product of SPT and heads 
PPRECMP EQU   BLKRD+8   ;HD Precomp value (SASI only)
PMAXDR  EQU   BLKRD+9   ;No of EOS partitions?
PDRVSZ  EQU   BLKRD+10  ;Size of each EOS partition?
POFFSET EQU   BLKRD+20  ;Offset to each HD partition?
PCPM    EQU   BLKRD+43  ;Start of CPM Track Offset

;
;       AdamNet Messages where N = Node ID
;        0N     -       reset node
;        1N     -       request status
;        3N     -       clear
;        4N     -       receive
;        6N     -       send
;       0DN     -       ready
;
;       The disk drive code comprises two separate devices, the net handler
;       and the disk handler.  The net handler is only triggered when the 6803
;       receives a byte over AdamNet.  It interprets the AdamNet command, acts
;       on the command (including sending and receiving blocks of data over the
;       Net) and sets the appropriate flags and data blocks in memory.  The 
;       disk handler is normally in control of the drive.  It initializes the
;       various flags and hardware and then loops, monitoring the data request 
;       flag.  When it finds a data request, it reads or writes to the disk 
;       drive, depending upon the read/write flag.  If the disk handler gets
;       a request to write to block FACEH, it does a format.
;
;AdamNet procedure to write to disk:
;  1)   Master node uses send command to send block specifications to disk drive. 
;       Block spec. is 5 bytes, 4 bytes block number (LSB ... MSB) and one byte
;       device spec (0 = primary device, NZ = secondary device). The net
;       handler puts all blocks less than 400H in message buffer.  Blocks of
;       400H go in external memory.
;  2)   Master node uses send command to send data block to disk.
;  3)   Net handler sets data request and write flags and returns to disk
;       handler.
;  4)   Disk handler sees flags, reads the block specification and writes
;       external memory to disk.
;
;AdamNet procedure to read from disk:
;  1)   Master node uses send command to send block specifications to disk drive. 
;  2)   Master node uses receive command to tell disk drive to send data block.
;  3)   Net handler sets data request and read flags and returns to disk
;       handler.
;  4)   Disk handler sees flags, reads the block specification and reads data
;       from disk to external memory.
;  5)   Meanwhile master node queries drive with RDY command.
;  6)   Drive ACKs ready when disk handler has comleted task.
;  7)   Master node then uses CLEAR command to tell net handler to send data.
;  8)   Net handler now sends data in external memory
;

;       Code starts halfway through PROM - first half unused

	ORG     0F800H          

	TBA                     ;unused
SCIINT:
	TST     DATREQ          ;is there an active data request
	BNE     SCINT3          ;if none, finish up, ignoring Net
	JSR     RDSCI           ;read byte from SCI
	BCS     SCINT4          ;jump if good read (and good net address)
	BVC     SCINT3          ;if address bad, finish up, ignoring Net
				;must be bad read, so NACK it
	LDAA    #$2             ;enable Tx interrupt
	STAA    SCICSR
SCINT1:
	LDX     #$0E            ;set wait count
	LDAA    #$8             ;get ready to test bit 3
SCINT2:
	BITA    P2DATA          ;is bit 3 of port 2 data set by SCI
	BEQ     SCINT1          ;no, loop until it's set
	DEX                     ;make sure it stays set long enough
	BNE     SCINT2          ;by looping until IX = 0
	LDAA    #$1B            ;enable Rx, RxI, Wakeup
	STAA    SCICSR
	TST     CMDSTG          ;are we in the middle of a command
	BEQ     SCINT3          ;no, just reset SCI
	CLRA                    ;clear command stage flag
	STAA    CMDSTG
	JMP     NACK            ;send NACK
SCINT3:
	LDAB    #$1B            ;enable Rx, RxI, Wakeup
	STAB    SCICSR
	CLRA                    ;clear command stage flag
	STAA    CMDSTG
	JMP     SCIEND          ;reset SCI
SCINT4:
	LDAB    CMDSTG          ;get the command stage flag in B
	ASLB                    ;multiply by 2
	LDX     #TBL1           ;point to command jump table
	ABX                     ;add offset in B
	LDX     $0,X            ;get address from table
JMPIX:
	JMP     $0,X            ;jump to address
TBL1:
	DW      STAGE0
	DW      STAGE1
	DW      STAGE2
	DW      STAGE3
	DW      STAGE4
	DW      STAGE5
STAGE0:
	ANDA    #$0F0           ;mask off lower nibble
	CMPA    #$0             ;is it NetReset
	BNE     NOTRST          ;no, check other commands
	SEC                     ;set carry
	JSR     SETREQ
	JMP     SCIEND          ;reset SCI
NOTRST:
	CMPA    #$10            ;is it status request?
	BEQ     STATUS          ;yes, jump
	CMPA    #$30            ;is it clear
	BNE     NOTCLR          ;no, jump
	JMP     CLEAR
NOTCLR:
	CMPA    #$40            ;is it Rx
	BNE     NOTRX           ;no, jump
	LDX     #$5             ;counter to test 5 bytes
RX1:
	LDAA    $7F,X           ;compare location 7F+X
	CMPA    $84,X           ;to location 84+X
	BNE     RX2             ;jump if different
	DEX                     ;check counter
	BNE     RX1             ;loop if more bytes to check
	BRA     ACK             ;all the same to send ACK
RX2:
	LDAA    #$0B
	CLC
	JSR     SETREQ
	JMP     SCIEND
NOTRX:
	CMPA    #$60            ;is it send
	BNE     NOTSND          ;no, jump
	LDAA    #$1             ;now stage 0
	STAA    CMDSTG          ;so set it
	JMP     SCIEND          ;reset SCi
NOTSND:
	CMPA    #$0D0           ;is it RDY
	BEQ     ACK             ;yes, send ACK
	JMP     SCIEND          ;else reset SCI (ignore command)
NACK:
	LDAA    #$0C0           ;NACK in upper nibble of A
	ORAA    DEVNUM          ;node ID in lower nibble of A
	JSR     TXBYTE          ;send it
	BCS     NACK1           ;if bad send
	JSR     RXBYTE          ;get response
	BCS     NACK1           ;if bad receive
	JMP     SCIEND          ;reset SCI
NACK1:
	JMP     SCIND1          ;return from interrupt
ACK:
	LDAA    #$90            ;ACK in upper nibble of A
	ORAA    DEVNUM          ;node ID in lower nibble
	JSR     TXBYTE          ;send it
	BCS     ACK1            ;if bad send
	JSR     RXBYTE          ;get response
	BCS     ACK1            ;if bad receive
	JMP     SCIEND          ;reset SCI
ACK1:
	JMP     SCIND1          ;return from interrupt
STATUS:
	LDD     TBL2            ;load double accumulator with 8000H
				;(status + MSB of msg len)
	ORAA    DEVNUM          ;put device number in lower nibble of A (=80)
	STD     MSTAT           ;save in buffer for status message
	LDD     TBL3            ;load double accumulator with 401H
	STD     MSTAT+2         ;put in 91 and 92
	JSR     GETSTAT         ;put double stat in 93
	LDX     #MSTAT          ;point to string to send
	LDD     #$5             ;load double accumulator with string length
	STD     STRLEN          ;save string length
	LDAA    #$80            ;initialize checksum
	ORAA    DEVNUM
	STAA    CHKSUM
	JSR     TXSTR           ;send response
	BCS     STAT1           ;if error
	LDAA    #$1
	STAA    L0096           ;put 1 if ???? flag - not used anywhere else
	JMP     SCIEND          ;reset SCI
STAT1:
	JMP     SCIND1          ;jump to RTI
CLEAR:
	TST     CURMSG+4        ;was the last command for primary device
	BEQ     CLR1            ;yes, skip next
	LDAA    SSTAT           ;so load secondary device status
	BRA     CLR2            ;skip next
CLR1:
	LDAA    PSTAT           ;get primary device status
CLR2:
	CMPA    #$3             ;test status
	BCC     CLR3            ;if 3 or greater, jump
	LDX     #BLKRD          ;get ready to read external memory
	LDD     #$0400          ;400H to send
	STD     STRLEN          ;so set message length
	BRA     CLR4            ;skip next
CLR3:
	JSR     GETSTAT         ;put combined status message in CSTAT
	LDX     #CSTAT          ;point to CSTAT
	LDD     #$1             ;message length = 1
	STD     STRLEN
CLR4:
	JSR     SNDMSG          ;send message (Block or error)
	BCS     CLR8            ;if error
	TST     CURMSG+4        ;was last for primary device
	BEQ     CLR5            ;yes, skip next
	LDAA    SSTAT           ;get secondary status
	BRA     CLR6            ;skip next
CLR5:
	LDAA    PSTAT           ;get primary status
CLR6:
	CMPA    #$1             ;test status
	BNE     CLR7            ;if not 1, jump
	LDAA    #$0FF           ;set external memory contents bad
	STAA    LSTMSG+4
CLR7:
	JMP     SCIEND          ;exit
CLR8:
	JMP     SCIND1          ;do simple RTI
STAGE1:
				;A = MSB of message length
	STAA    STRLEN          ;save MSB of message length
	LDAA    #$2             ;activate stage 2
	STAA    CMDSTG
	JMP     SCIEND          ;reset SCI
STAGE2:
				;A = LSB of message length
	STAA    STRLEN+1        ;save LSB of message length
	CMPA    #$5             ;is it 5?
	BNE     STAG21          ;no, it must be 400H
	LDAA    STRLEN          ;get MSB of message length
	BEQ     STAG22          ;if it's 0, ie 0005, jump
STAG21:
	LDAA    #$3             ;activate stage 3
	STAA    CMDSTG
	LDD     #BLKWRT         ;block to go at 400H
	STD     BLKLOC
	JMP     SCIEND          ;reset SCI
STAG22:
	LDAA    #$4             ;activate stage 4
	STAA    CMDSTG
	LDD     #$80            ;block to go at 80H
	STD     BLKLOC
	CLR     CHKSUM          ;clear the checksum
	JMP     SCIEND          ;reset SCI
STAGE3:
				;A = first byte of message
	LDAB    #$4             ;activate stage 4
	STAB    CMDSTG
	CLR     CHKSUM          ;clear checksum
STAGE4:
				;A = message byte
	LDX     BLKLOC          ;get memory location to save message
	STAA    $0,X            ;save there
	INX                     ;increment pointer
	STX     BLKLOC          ;save it
	EORA    CHKSUM          ;calculate checksum
	STAA    CHKSUM          ;save it
	LDD     STRLEN          ;get message length
	SUBD    #$1             ;decrement it
	STD     STRLEN          ;save it
	BNE     SCIEND          ;reset SCI if not done
	LDAA    #$5             ;else activate stage 5
	STAA    CMDSTG
	BRA     SCIEND          ;and reset SCI
STAGE5:
				;A = message checksum
	LDAB    #$0             ;reset commmand stage to 0
	STAB    CMDSTG
	CMPA    CHKSUM          ;check against calculated checksum
	BEQ     STAG51          ;if they're the same jump
	LDAA    #$2             ;else set primary status to 2
	STAA    PSTAT
	JMP     NACK            ;and send NACK
STAG51:
	LDX     BLKLOC          ;check if loaded at 80H or 400H
	CPX     #$0100
	BCS     STAG52          ;carry set if 80H, so just send ACK
	LDAA    #$0C            ;set data write
	STAA    RDWRT
	LDAA    #$0AA           ;data request flag
	STAA    DATREQ
STAG52:
	JMP     ACK             ;send ACK
SNDMSG:
	LDAA    #$0B0           ;Minor node send
	ORAA    DEVNUM          ;or in Node ID
	JSR     TXBYTE          ;send it
	BCS     TXSTR1          ;if bad send
	LDAA    STRLEN          ;get message length MSB
	JSR     TXBYTE          ;send it
	BCS     TXSTR1          ;if bad send
	LDAA    STRLEN+1        ;get message length LSB
	JSR     TXBYTE          ;send it
	BCS     TXSTR1          ;if bad send
	CLR     CHKSUM          ;clear checksum and fall through to send message
TXSTR:
	LDAA    $0,X            ;get byte pointed to by IX
	JSR     TXBYTE          ;send it
	BCS     TXSTR1          ;jump if error
	EORA    CHKSUM          ;generate checksum
	STAA    CHKSUM          ;save it
	INX                     ;increment pointer
	LDD     STRLEN          ;load DA with string length
	SUBD    #$1             ;decrement it
	STD     STRLEN          ;save string length
	BNE     TXSTR           ;send more if leength > 0
	LDAA    CHKSUM          ;get checksum
	JSR     TXBYTE          ;send it
	BCS     TXSTR1          ;if error
	JSR     RXBYTE          ;get response
TXSTR1:
	RTS
SCIEND:
	TST     DATREQ          ;test SCI status flag
	BEQ     SCIND1          ;if it's 0 exit, SCI routine
	LDAA    #$0A            ;enable Rx & Tx, but disable interrupts
	STAA    SCICSR
	LDAA    SCICSR          ;read status to reset
	LDAA    SCIRXD          ;read receive to reset
	JSR     RXBYTE          ;read byte from Net
SCIND1:
	RTI                     ;return from SCI interrupt
GETSTAT:
	LDAA    SSTAT           ;get contents of 95 in A
	ASLA                    ;put in upper nibble
	ASLA
	ASLA
	ASLA
	ORAA    PSTAT           ;put 94 in lower nibble
	STAA    CSTAT           ;save in 93
	RTS
TBL2:
	DW      $8000
TBL3:
	DW      $401
	DB      $0
RDSCI:
	SEC                     ;set carry flag
	LDAB    SCICSR          ;get SCI status in B
	LDAA    SCIRXD          ;read SCI data
	ANDB    #$40            ;check for read errors, carry not affected
	BEQ     RDSCI1          ;branch if no error
	CLC                     ;clear the carry
	SEV                     ;set overflow
	RTS
RDSCI1:
	LDAB    CMDSTG          ;check if in middle of a command
	BNE     RDSCI3          ;branch if so
	TAB                     ;transfer Node ID to B
	ANDA    #$0F            ;look at lower nibble of ID
	CMPA    DEVNUM          ;is it us
	BNE     RDSCI2          ;jump if not
	TBA                     ;put byte back in A
	SEC                     ;set the carry
	BRA     RDSCI3          ;clear next
RDSCI2:
	CLC                     ;clear carry
RDSCI3:
	CLV                     ;clear overflow
	RTS
TXBYTE:
	PSHX                    ;save IX
	LDX     #$16            ;store count for timeout
TXBYT1:
	DEX                     ;decrement count
	BEQ     TXBYT2          ;timeout if count exhausted
	LDAB    SCICSR          ;get SCI status
	ANDB    #$20            ;is Tx register full
	BEQ     TXBYT1          ;wait if it is
	STAA    SCITXD          ;put byte in Tx register (send it)
	CLC                     ;clear the carry
	BRA     TXBYT3          ;exit
TXBYT2:
	JSR     SCIRES          ;reset SCI
	SEC                     ;set the carry
TXBYT3:
	PULX                    ;restore IX
	RTS
RXBYTE:
	PSHX                    ;save IX
	LDX     #$22            ;count for timeout
RXBYT1:
	DEX                     ;decrement count
	BEQ     RXBYT3          ;timeout if count exhausted
	LDAB    SCICSR          ;get SCI status
	ANDB    #$20            ;check if last byte transmitted yet
	BEQ     RXBYT1          ;wait until it is
	LDAB    SCIRXD          ;read Rx data
RXBYT2:
	DEX                     ;decrement counter
	BEQ     RXBYT3          ;if timed out
	LDAB    SCICSR          ;read SCI status
	BPL     RXBYT2          ;loop if bit 7 is not set
	LDAB    SCIRXD          ;read byte
	PULX                    ;restore IX
	RTS
RXBYT3:
	BSR     SCIRES          ;reset SCI
	PULX                    ;restore IX
	RTS
SCIRES:
	LDAB    SCICSR          ;read SCI status
	LDAB    SCIRXD          ;clear any received data
	LDAB    #$1B            ;enable Rx, Tx, Wakeup
	STAB    SCICSR
	LDAB    #$0             ;reset command stage to 0
	STAB    CMDSTG
	RTS
SETREQ:
	BCC     SETRQ1          ;if carry clear, ie if not called by reset command
	LDAA    #$0FF           ;put FF in DATREQ
	STAA    DATREQ
	BRA     SETRQ2          ;return
SETRQ1:
	STAA    RDWRT           ;put's 0BH in RDWRT to force a read
	LDAA    #$0AA           ;put AA in DATREQ
	STAA    DATREQ
	LDAA    #$0B            ;disable receive interrupt
	STAA    SCICSR
SETRQ2:
	RTS
;
;       Initialize Hard Disk routine
;
RESET:
	SEI                     ;disable interrupts
	LDS     #$0E8           ;set stack pointer to E8
	LDAA    #$00            ;set port 1 data direction to 00000000
	STAA    P1DDR           ; (all inputs)
	LDAA    #$0FF           ;set port 4 data direction to 11111111
	STAA    P4DDR           ; (all outputs)
	LDAA    #$15            ;set port 2 data direction to 00010101
	STAA    P2DDR           ; (P20-P22 & P24 input, P25 output)
	LDAA    #$4             ;set SCI rate & mode 62.5 kbaud, NRZ,
				; internal clock, post2 bit 2 not used
	STAA    RMCR
	LDAA    #$1B            ;enable Rx, Tx, Wakeup
	STAA    SCICSR
	LDAA    #$0FF           ;enable internal ram
	STAA    RAMCR
	LDX     #$0FF           ;start clearing memory at 0FFH
RESET1:
	CLR     $0,X            ;reset memory
	DEX                     ;decrement pointer
	CPX     #$80            ;is it 80H
	BCC     RESET1          ;no, loop
	DEC     LSTMSG+4        ;set lstmsg+4 to 0FFH
;
;       Set device number (DRVNO or DRVNO+1)
;
	LDAA    P1DATA          ;read port 1
	ANDA    #$80            ;is switch on back set to drive ID 2?
	CLC                     ;clear carry
	ROLA                    ;move bit 7 to bit 1
	ROLA
	ORAA    #DRVNO          ;or in base drive ID 
	STAA    DEVNUM          ;save it
;
;       Init hard drive
;
	JSR     TBUSY           ;Wait for drive to become ready
	LDAA    #HRESET         ;Set RESET bit in control register
	STAA    WRCONT
	NOP                     ;Wait
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP        
        LDAA    #HNOIRQ         ;Clear RESET bit and 
	STAA    WRCONT          ; Turn off hard disk interrupt
	JSR     TBUSY           ;Wait for drive to complete action
	LDAA    #HRECAL         ;Home drive
	STAA    WRCMD
	JSR     TBUSY           ;Wait for drive to home
;
	JSR     GETPARM         ;Go get HD parameters
;
;       Set Hard Disk parameters
;
	CLRA                    ;Zero MSB of HNHSPT and HSPT
	LDAB    PSPT            ;Get no of HD sectors per track
	STD     HSPT            ;Save for later use here
	STAB    WRSECCT         ;Send to controller for STPARM
	LDAB    PNHSPT          ;Get LSB of no of HDs x SPT
	STD     HNHSPT          ;Save word for later use here
	LDD     PTRACK          ;Get no of HD cylinders
	STAA    WRCYLLO         ;Send to controller for STPARM
	STAB    WRCYLHI
	LDAA    PSEEK           ;Get No of heads (stored in PSEEK)
	ANDA    #$0F            ;Get rid of upper nibble
	ORAA    #$0A0           ;Always select drive 1
	STAA    WRSDH           ;Send to controller for STPARM
	LDAA    #HSTPARM        ;Send Set Parameters command to HD
	STAA    WRCMD
	JSR     TBUSY           ;Wait for drive to finish operation
	LDAA    RDSTAT          ;Get drive status
	ANDA    #$1             ;Mask out ERROR bit
	BEQ     RESET2          ;Good, continue
	JMP     MISMED          ;No good, go report
RESET2:
	JMP     DSKRES          ;Go enable interrupts for Adamnet
				; and jump to MAIN when finished
;
;       Wait until current hard disk operation is complete
;
TBUSY:  
	LDAA    RDSTAT          ;Get contents of Status register
	EORA    #$50H           ;Invert RDY and SC bits (6 & 4)
	ANDA    #$0D2H          ;Look only at BSY, \RDY, \SC & IDX bits
	BNE     TBUSY           ;Loop if any are set (high)
	RTS                     ;Return when all are clear (low) 
;
;       Get Hard Disk parameters
;
GETPARM:
	CLRA
	STAA    WRCYLLO         ;Set cylinder = 0
	STAA    WRCYLHI
	INCA
	STAA    WRSECNO         ;Set sector = 1
	STAA    WRSECCT         ;Get one sector
	LDAA    #$A0            ;Select HD0, Head 0
	STAA    WRSDH
;
;       Get Hard Disk parameters
;
	LDAA    #HREAD
	STAA    WRCMD           ;Send read command to drive
	JSR     TBUSY           ;Wait for drive to finish
	LDAA    RDSTAT          ;Get drive status
	ANDA    #$1             ;Mask out ERROR bit
	BEQ     GTPARM2         ;Good, continue
	JMP     CRC          ;No good, go report
GTPARM2:        
	LDX     #BLKWRT         ;Good, Point at Write Buffer
	CLRA                    ;Zero word counter
GTPARM3:        
	LDAB    RDDATLO         ;Get sector data (512 bytes)
	STAB    0,X             ;Get lower byte & store in buffer
	INX                     ;Point at next buffer location
	LDAB    RDDATHI         ;Get upper byte    
	STAB    0,X             ;Store in buffer
	INX                     ;Point at next buffer location
	DECA                    ;Decrement word counter
	BNE     GTPARM3         ;More words left; get next one
	RTS                     ;Finished, return to calling pgm
;
;       Loop here waiting for ADAMnet data I/O request from
;       ADAMnet interrupt handler
;
MAIN:
	LDAA    DATREQ          ;is there a data request
	BEQ     MAIN            ;no, return to start of main loop
	CMPA    #$0FFH          ;yes, is it a reset drive request?
	BNE     MAIN1           ;no, continue
	JMP     RESET           ;yes, go reset drive
MAIN1:  
	LDAB    CURMSG+4        ;is the primary device requested?
	BEQ     DODATA          ;yes, skip next
	CLR     DATREQ          ;clear data request
	BRA     MAIN            ;loop some more

DODATA:
	SEI                     ;disable interrupts
	LDAA    RDWRT           ;test read/write flag
	CMPA    #$0C            ;is it write?
	BNE     RDBLK           ;no, go do read
	LDX     CURMSG          ;yes, get requested block
	CPX     #$53CA          ;is it a CA53 request?
	BNE     CHKFMT          ;no, check if it is format
	BRA     NOTFMT          ;yes, skip next
CHKFMT:
	CPX     #$0CEFA         ;is it format = FACEH
	BEQ     CHKFMT2         ;yes, go finish
	JMP     WRBLK           ;no, go write block
CHKFMT2:        
	LDAA    #$0FF           ;yes, set all message bytes to 0FFH
	LDAB    #$0FF
	STD     LSTMSG
	STD     LSTMSG+2
	STAA    LSTMSG+4
	STD     CURMSG
	STD     CURMSG          ;should be +2 #######
	STAA    CURMSG          ; and +4
NOTFMT:
	CLR     DATREQ          ;clear data request flag
	JMP     OKRDWR          ;finish up with no error
;
;       Read a block (2 sectors - 1024 bytes) from disk
;
RDBLK:
	JSR     TBUSY           ;Wait until controller ready
	JSR     CLCCHS          ;Go calculate Cyl, Hd, Sector
				;  and send them to hard drive
	LDAA    #HREAD          ;Go get first sector
	STAA    WRCMD           ;Send read command to drive
	JSR     TBUSY           ;Wait for drive to finish
	LDAA    RDSTAT          ;Get drive status
	ANDA    #$1             ;Mask out ERROR bit
	BEQ     RDBLK0          ;Good, continue
	JMP     MISBLK          ;No good, go report
RDBLK0:        
	LDAA    RDSTAT          ;Good, get HD controller status
	ANDA    #$8             ;Mask out DRQ bit, 1 = send data
	BEQ     RDBLK0          ;Wait for drive to request data
	LDX     #BLKWRT         ;OK, point at Write Buffer
	CLRA                    ;Zero word counter
RDBLK1:        
	LDAB    RDDATLO         ;First 256 words (512 bytes)
	STAB    0,X             ;Get lower byte & store in buffer
	INX                     ;Point at next buffer location
	LDAB    RDDATHI         ;Get upper byte    
	STAB    0,X             ;Store in buffer
	INX                     ;Point at next buffer location
	DECA                    ;Decrement word counter
	BNE     RDBLK1          ;More words left; get next one
;        
;       Get Second Sector
;
	JSR     TBUSY           ;Wait until controller ready
	JSR     CLCNXT          ;Done, Calculate next Cyl, Hd, Sec
				;  and send them to hard drive
	LDAA    #HREAD          ;Send read command to drive
	STAA    WRCMD
	JSR     TBUSY           ;Wait for drive to finish
	LDAA    RDSTAT          ;Get drive status
	ANDA    #$1             ;Mask out ERROR bit
	BEQ     RDBLK2          ;Good, continue
	JMP     MISBLK          ;NG, go report missing block
RDBLK2:        
	LDAA    RDSTAT          ;Good, get HD controller status
	ANDA    #$8             ;Mask out DRQ bit, 1 = send data
	BEQ     RDBLK2          ;Wait for drive to request data
	LDX     #BLKWRT+512     ;OK, point at second half of buffer
	CLRA                    ;Zero word counter
RDBLK3:        
	LDAB    RDDATLO         ;Second 256 words (512 bytes)
	STAB    0,X             ;Get lower byte & store in buffer
	INX                     ;Point at next buffer location
	LDAB    RDDATHI         ;Get upper byte    
	STAB    0,X             ;Store in buffer
	INX                     ;Point at next buffer location
	DECA                    ;Decrement word counter
	BNE     RDBLK3          ;More words left; get next one
RDBLK4:                         ;Finished, wrap up
	LDD     CURMSG          ;Transfer current message to
	STD     LSTMSG          ; last message
	LDD     CURMSG+2
	STD     LSTMSG+2
	LDAA    CURMSG+4
	STAA    LSTMSG+4
	BRA     OKRDWR          ;finish up, no error
;
;       Write a block (2 sectors - 1024 bytes) to disk
;
WRBLK:                          ;Send first sector 
	JSR     TBUSY           ;Wait until controller ready
	JSR     CLCCHS          ;Go Calculate Cyl, Hd, Sector
				;  and send them to the drive
	LDAA    #HWRITE         ;Send write command to drive
	STAA    WRCMD  
	JSR     TBUSY           ;Wait for drive to finish
	LDAA    RDSTAT          ;Get drive status
	ANDA    #$1             ;Mask out ERROR bit
	BNE     MISBLK          ;No good, go report
WRBLK0:
	LDAA    RDSTAT          ;Good, get HD controller status
	ANDA    #$8             ;Mask out DRQ bit, 1 = send data
	BEQ     WRBLK0          ;Wait for drive to request data
	LDX     #BLKRD          ;OK, Point at Read Buffer
	CLRA                    ;Zero word counter
	INX                     ;Point at high byte
WRBLK1:
	LDAB    0,X             ;Get high byte
	STAB    WRDATHI         ;Send it to drive
	DEX                     ;Point at low byte
	LDAB    0,X             ;Get low byte
	STAB    WRDATLO         ;Send it to drive
	INX                     ;Point back at high byte
	INX                     ;Point to next low byte
	INX                     ;Point to next high byte
	DECA                    ;Decrement word count
	BNE     WRBLK1          ;More words, go send them
WRBLK2:                         ;Send second sector
	JSR     TBUSY           ;Wait until controller ready
	JSR     CLCNXT          ;Done, calculate next Cyl, Hd,
				;  Sector and send to the drive
	LDAA    #HWRITE         ;Send write command to drive
	STAA    WRCMD  
	JSR     TBUSY           ;Wait for drive to finish
	LDAA    RDSTAT          ;Get drive status
	ANDA    #$1             ;Mask out ERROR bit
	BNE     MISBLK          ;No good, go report
WRBLK3:
	LDAA    RDSTAT          ;Good, get HD controller status
	ANDA    #$8             ;Mask out DRQ bit, 1 = send data
	BEQ     WRBLK3          ;Wait for drive to request data
	LDX     #BLKRD+512      ;Good, point @ 2nd half of Rd Buff
	CLRA                    ;Zero word counter
	INX                     ;Point at high byte
WRBLK4:
	LDAB    0,X             ;Get high byte
	STAB    WRDATHI         ;Send it to drive
	DEX                     ;Point at low byte
	LDAB    0,X             ;Get low byte
	STAB    WRDATLO         ;Send it to drive
	INX                     ;Point back at high byte
	INX                     ;Point to next low byte
	INX                     ;Point to next high byte
	DECA                    ;Decrement word count
	BNE     WRBLK4          ;More words, go send them
;
;       Read/Write clean-up routines
;
OKRDWR:
	CLRA                    ;no error so clear A
	BRA     DSKDN
MISMED:
	LDD     CURMSG          ;transfer current message to last message
	STD     LSTMSG
	LDD     CURMSG+2
	STD     LSTMSG+2
	LDAA    CURMSG+4
	STAA    LSTMSG+4
	LDAA    #$3             ;status = missing media
	BRA     DSKDN
CRC:
	LDAA    #$1             ;status = CRC error
	BRA     DSKDN
MISBLK:
	LDAA    #$2             ;status = missing block error
DSKDN:
	STAA    PSTAT
	BRA     DSKDN1
;
	STAA    SSTAT           ;unused code for secondary device
DSKDN1:
	CLR     DATREQ
DSKRES:
	LDAA    #$1B            ;enable Tx, Rx, Wakeup
	STAA    SCICSR
	CLRA
	STAA    CMDSTG          ;reset command stage
	CLI                     ;enable interrupts
	JMP     MAIN            ;wait loop
;
;       Calculate Cylinder, Sector and Head from Block Number
;
CLCCHS:
	LDAB    CURMSG          ;Get ADAMnet block number into
	LDAA    CURMSG+1        ; Accumulators for double byte math
	CLR     THREEB          ;Assume 2 byte SEC# (THREEB = 0)
	LSLD                    ;Multiply X2 for 512 byte sectors
	BCC     CLCHS1          ;Skip next if SEC# < 64K (2 bytes)
	DEC     THREEB          ;3 byte SEC#, set #BYTE = FF
CLCHS1:
	LDX     #$0FFFFH        ;Initialize Index Register (will 
	CLR     REQHD           ; hold REQCYL) and Head storage
	DEC     REQHD           ; location for math
CLCHS2:
	SUBD    HNHSPT          ;Divide REQBLK by SEC*HDS until
	INX                     ; overflow, # of passes thru loop
	BCC     CLCHS2          ; provides CYL number in Index Reg
	TST     THREEB          ;See if have 2 or 3 byte sec #
	BEQ     CLCHS3          ;2 byte, done with SECHDS divide
	CLR     THREEB
	BRA     CLCHS2          ;3 byte, continue SECHDS divide
CLCHS3:                         ;D has remainder, X has REQCYL#
	PSHX                    ;Save REQCYL#
	ADDD    HNHSPT          ;Add back last subtraction
CLCHS4:
	SUBD    HSPT            ;Divide remainder by Sec/Trk until
	INC     REQHD           ; overflow, # passes thru loop
	BCC     CLCHS4          ; provides HD # in REQHD
	ADDD    HSPT            ;Add back last subtraction
	INCB                    ;Add one sector for number conversion
	STAB    REQSEC          ;Remainder is SEC#, save for later use
	STAB    WRSECNO         ;Send it to the drive
	PULA                    ;Get REQCYL# back
	PULB                    ; - LSB in B, MSB in A
	STD     REQCYL          ;Save for later use
	STAB    WRCYLLO         ;Send REQCYL# LSB to drive
	STAA    WRCYLHI         ;Send REQCYL# MSB to drive
	LDAA    #$1             ;Set up for single sector access
	STAA    WRSECCT         ;Set drive sector count to 1
	LDAA    REQHD           ;Get Head # to access
	ORAA    #$A0            ;Select HD0
	STAA    WRSDH           ;Send to drive
	RTS                     ;Return to calling program
;
;       Calculate second sector's address (Cyl, Hd, Sec)
;
CLCNXT:

;       Set SECCT to 1 before anything else?
;       Do I have the order right? SEC -> HD -> CYL?

	LDAA    #1
	STAA    WRSECCT         ;One sector transfer
	LDAA    REQSEC          ;Get requested sector back
	INCA                    ;Point at next sector
	CMPA    HSPT+1          ;See if greater than Max Sec#
	BGT     CLCNXT1         ;Yes, go correct HD#
	STAA    REQSEC          ;No, okay as is - save it
	STAA    WRSECNO         ;Write it to the hard disk
	RTS                     ;Finished
CLCNXT1:
	LDAA    #1              ;Start at beginning of next trk
	STAA    REQSEC          ;Save it for later use
	STAA    WRSECNO         ;Write it to the hard disk
	LDAA    REQHD           ;Get back last head number
	INCA                    ;Increment it
	CMPA    PSEEK           ;See if greater than #HDs
	BGT     CLCNXT2         ;Yes, go correct CYL#
	ORAA    #$A0            ;No, mask in HD0 select
	STAA    WRSDH           ;Send to hard disk
	RTS                     ;Finished
CLCNXT2:
	CLR     REQHD           ;Start at first head
	LDAA    #$A0            ;Send first head # to HD
	STAA    WRSDH
	LDD     REQCYL          ;Get previous CYL# to modify
	ADDD    #1              ;Increment to next Cylinder
	STD     REQCYL          ;Save it back again
	STAB    WRCYLLO         ;Write it to the hard disk 
	STAA    WRCYLHI
	RTS                     ;Finished
;
;       Interrupt vectors
;
	ORG     0FFEFH

RETI:
	RTI                     ;return from interrupt
	DW      SCIINT          ;IRQ2/SCI (RDRF,ORFE,TDRE)
	DW      RETI            ;IRQ2/Timer overflow
	DW      RETI            ;IRQ2/Timer output compare
	DW      RETI            ;IRQ2/Timer input capture
	DW      RETI            ;IRQ1 interrupt (IRQ1 - IS3)
	DW      RETI            ;software interrupt (SWI)
	DW      RETI            ;Non-maskable Interrupt
	DW      RESET           ;reset
	END
